# 高阶函数
# 函数名本身也是一个变量
f = abs
print(f(-10))
print(abs)

# 高阶函数就是可以接收函数作为参数的函数
def add(x, y, f):
    return f(x) + f(y)

print(add(-3, -9, f))

# map函数
# 将函数作用于 Iterable 的每个元素，并将结果作为 Iterator 进行返回
# reduce函数
# 将函数作用的当前结果和 Iterable 的下一个元素继续做累计运算

# 写一个str2int函数
from functools import reduce
def char2num(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]
def str2int(s):
    return reduce(lambda x, y : x * 10 + y, map(char2num, s))

print(str2int('12341432534'))


# experiment
def normalize(name):
    tmp = name[0].upper() + name[1:].lower()
    return tmp

# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)

from functools import reduce

def prod(L):
    return reduce(lambda x, y : x * y, L)

print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
if prod([3, 5, 7, 9]) == 945:
    print('测试成功!')
else:
    print('测试失败!')

from functools import reduce

def char2num(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]

def extract(s):
    parts = s.split('.')
    if len(parts) == 2:
        integer_part = parts[0]
        decimal_part = parts[1]
    elif len(parts) == 1:
        integer_part = parts[0]
        decimal_part = '0'
    return (integer_part, decimal_part)

def str2float(s):
    integer_str, decimal_str = extract(s)
    integer_num = map(char2num, integer_str)
    decimal_num = map(char2num, reversed(decimal_str))
    integer = reduce(lambda x, y: x * 10 + y, integer_num)
    decimal = reduce(lambda x, y: x * 0.1 + y, decimal_num)
    return integer + 0.1 * decimal 

print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
    print('测试成功!')
else:
    print('测试失败!')

# filter函数，也接受一个函数和一个序列，这个序列可以是 Iterable or Iterator
def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n

def _not_divisible(n):
    return lambda x: x % n > 0

def primes():
    yield 2
    it = _odd_iter()
    while True:
        n = next(it)
        it = filter(_not_divisible(n), it)
        yield n

for n in primes():
    if n < 100:
        print(n)
    else:
        break


# experiment
def is_palindrome(n):
    s = str(n)
    if len(s) <= 1:
        return True
    l = len(s)
    str_pre = s[:l // 2]    
    if l % 2 == 0:
        str_back = s[l // 2:]
    else:
        str_back = s[l // 2 + 1:]
    return str_pre == str_back[::-1]
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:
    print('测试成功!')
else:
    print('测试失败!')

# sorted函数也是高阶函数，可以接受一个 key 函数作为排序的依据，默认是升序，如果是降序，指定 reverse = True 即可

print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True))

# experiment
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]

def by_name(t):
    return t[0]

L2 = sorted(L, key=by_name)
print(L2)

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]

def by_score(t):
   return t[1] 

L2 = sorted(L, key=by_score, reverse=True)
print(L2)
